# vim: expandtab:ts=4:sw=4
class TrackState:
    """
    tentative
        不确定状态，初始化Track的时候分配，只有在连续matched超过3次，才会变成confirmed
        如果超过3次都没有match，那么就会变成deleted态。
    confirmed
        确定态，代表Track是匹配状态。当Track处于confirmed，连续unmatched达到max_age次，
        就会转变为deleted。
    Deleted
        该track会被删除
    """
    Tentative = 1
    Confirmed = 2
    Deleted = 3

class Track:
    """
    一个目标跟踪track，状态空间向量为(x,y,a,h)，关联着它们的速度信息

    Parameters
    ----------
    n_init : int
        track被confirmed前的连续detections的数量，如果在开始的n_init帧内发生miss，
        则track的状态就会被设置为Deleted
    max_age : int
        track状态被设置为Deleted之前的最大连续miss次数。
    mean : ndarray
        初始状态分布的均值向量
    covariance : ndarray
        初始状态分布的协方差矩阵
    track_id : int
        track分配的唯一ID
    hits : int
        测量更新的总数
    age : int
        第一次发生以来的总帧数
    time_since_update : int
        自上次测量更新以来的总帧数。
    state : TrackState
        目前的track状态.
    features : List[ndarray]
        特征缓存。 在每次测量更新时，相关的特征向量都会添加到此列表中。
    """
    def __init__(self, mean, covariance, track_id, n_init, max_age,class_id,
                 feature=None):
        self.mean = mean    #均值
        self.covariance = covariance    #协方差
        self.track_id = track_id    #track的id
        self.class_id = class_id
        #===========初始化类别字典================
        self.classes = {}

        #hits代表匹配了多少次，成功连续匹配次数达到_n_init=3时才会将将状态设置为confirmed
        self.hits = 1
        self.age = 1    #age越大则越晚被跟踪，每次调用卡尔曼滤波预测时候就会+1

        #每次调用卡尔曼滤波预测时候就会+1,每次调动update的时候就会重置为0，意思就是每次成功匹配时，重置为0,否则+1
        self.time_since_update = 0

        self.state = TrackState.Tentative
        #每个track对应多个features，每次update都将最新的features添加到列表里
        self.features = []
        if feature is not None:
            self.features.append(feature)

        self._n_init = n_init
        self._max_age = max_age #最大存活期限

    def to_tlwh(self):
        """Get current position in bounding box format `(top left x, top left y,
        width, height)`.

        Returns
        -------
        ndarray
            The bounding box.

        """
        ret = self.mean[:4].copy()
        ret[2] *= ret[3]
        ret[:2] -= ret[2:] / 2
        return ret

    def to_tlbr(self):
        """Get current position in bounding box format `(min x, miny, max x,
        max y)`.

        Returns
        -------
        ndarray
            The bounding box.

        """
        ret = self.to_tlwh()
        ret[2:] = ret[:2] + ret[2:]
        return ret

    def predict(self, kf):
        """
        使用卡尔曼滤波器预测step，将状态分布传播到当前step。
        使用卡尔曼滤波的predict方法对每条track进行状态（8x8矩阵的均值和协方差）预测，
        基于上一时刻的状态对当前时刻的状态和不确定性进行预测。（根据线性运动学方程进行预测

        mean(u,v,y,h,x1,y1,y~,h~),x=vt,预测不确定性covariance）
        Parameters
        ----------
        kf : kalman_filter.KalmanFilter
            卡尔曼滤波器

        """
        #根据上一时刻的track状态预测出来当前时刻的均值和协方差计算
        self.mean, self.covariance = kf.predict(self.mean, self.covariance)
        #这个track每次predict时就将age+1,年龄越大的tracks越晚被跟踪。
        self.age += 1
        self.time_since_update += 1

    def update(self, kf, detection,class_id):
        """Perform Kalman filter measurement update step and update the feature
        cache.
        执行卡尔曼测量更新当前step，并且更新特征缓存

        Parameters
        ----------
        kf : kalman_filter.KalmanFilter
            The Kalman filter.
        detection : Detection
            The associated detection.

        """
        self.mean, self.covariance = kf.update(
            self.mean, self.covariance, detection.to_xyah())
        self.features.append(detection.feature)
        self.class_id = class_id

        self.hits += 1
        self.time_since_update = 0
        #如果track的状态为不确定Tentative，并且连续成功匹配3次，则，将其置为Confirmed
        if self.state == TrackState.Tentative and self.hits >= self._n_init:
            self.state = TrackState.Confirmed

    def mark_missed(self):
        """
        标记当前track为missed(当前的时间步中没有关联上).
        """
        if self.state == TrackState.Tentative:
            self.state = TrackState.Deleted
        elif self.time_since_update > self._max_age:
            self.state = TrackState.Deleted

    def is_tentative(self):
        """Returns True if this track is tentative (unconfirmed).
        """
        return self.state == TrackState.Tentative

    def is_confirmed(self):
        """Returns True if this track is confirmed."""
        return self.state == TrackState.Confirmed

    def is_deleted(self):
        """Returns True if this track is dead and should be deleted."""
        return self.state == TrackState.Deleted
